/******************************************************************************
 * program:     rasimg library 0.33                                           *
 * function:    Object set for handling vector images.                        *
 * modul:       vec_image.cc                                                  *
 * licency:     GPL or LGPL                                                   *
 * Copyright: (c) 2018-2025 Jaroslav Fojtik                                   *
 ******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "csext.h"

#include "matrix.h"

#include "vecimage.h"
#include "arith.h"


static void FixPsAccent(string & s, const char *TexAccent, const char *PsAccent);
static void FixPsAccent2(string & s, const char *TexAccent, const char *PsAccent);


/** Normalise angle after difference calculatiun to be in a range <-pi;+pi>
 * @param[in]	num1	Angle to normalise [deg].
 * @return		Normalised angle. */
float NormAngleDEG(float num1)
{  
  if(num1>180)
  {
    num1 += 180;
    num1 = num1 -  (360) * ((int)(num1/(360)));
    num1 -= 180;
  }
  else if(num1<-180)
  {
    num1 -= 180;
    num1 = num1 -  (360) * ((int)(num1/(360)));
    num1 += 180;
  }
return num1;
}


void UpdateBBox(FloatBBox & bbx, const float_matrix & corners)
{
  for(TSIZE i=0; i<corners.cols(); i++)
  {
    const float x = corners(i,0);
    const float y = corners(i,1);
    if(bbx.MinX>x) bbx.MinX=x;
    if(bbx.MaxX<x) bbx.MaxX=x;
    if(bbx.MinY>y) bbx.MinY=y;
    if(bbx.MaxY<y) bbx.MaxY=y;
      //printf("[%.1f,%.1f]", x, y);
  }
}


void InitBBox(FloatBBox & bbx)
{
  bbx.MinY = bbx.MinX = 65537; 
  bbx.MaxY = bbx.MaxX =-32767;
}


void UpdateBBox(FloatBBox & bbx, const AbstractTransformXY *Tr, const FloatBBox & bbxIn)
{
  float x, y;
  x=bbxIn.MinX;		y=bbxIn.MinY;
  if(Tr) Tr->ApplyTransform(x,y);
  UpdateBBox(bbx, 0, x,y, 0,0);
  x=bbxIn.MaxX;		y=bbxIn.MaxY;
  if(Tr) Tr->ApplyTransform(x,y);
  UpdateBBox(bbx, 0, x,y, 0,0);
  if(Tr)	// second diagonal makes sense only when shape is transformed.
  {
    x=bbxIn.MinX;		y=bbxIn.MaxY;
    Tr->ApplyTransform(x,y);
    UpdateBBox(bbx, 0, x,y, 0,0);
    x=bbxIn.MaxX;		y=bbxIn.MinY;
    Tr->ApplyTransform(x,y);
    UpdateBBox(bbx, 0, x,y, 0,0);
  }
}


/// @param[in,out]	bbx	Bounding box structure.
/// @param[in]		alpha	Rotation angle [deg].
/// @param[in]		x	Low left corner x coordinate.
/// @param[in]		y	Low left corner y coordinate.
/// @param[in]		dx	Width.
/// @param[in]		dy	Height.
void UpdateBBox(FloatBBox & bbx,
		float alpha, float x, float y, float dx, float dy)
{
  if(dx<0)
      {
      x += dx;
      dx = -dx;
      }
  if(dy<0)
      {
      y += dy;
      dy = -dy;
      }

  if(fabs(alpha)<1e-6)
      {
      if(bbx.MinX>x)    bbx.MinX = x;
      if(bbx.MinY>y)    bbx.MinY = y;
      if(bbx.MaxX<x+dx) bbx.MaxX = x+dx;
      if(bbx.MaxY<y+dy) bbx.MaxY = y+dy;
      return;
      }
  else
  {
    float_matrix m(3,3);

//     |1  0 -Sx|   |cos -sin 0|   |1  0 Sx|
// R = |0  1 -Sy| * |sin  cos 0| * |0  1 Sy|
//     |0  0   1|   |0     0  1|   |0  0  1|
    alpha = ((float)M_PI*alpha)/180.0f;	//calculate radians    
      {
      const float X0 = x+dx/2;
      const float Y0 = y+dy/2;
      const float CosAlpha = cos(alpha);
      const float SinAlpha = sin(alpha);
      m(0,0) = CosAlpha;
      m(1,0) =-SinAlpha;
      m(2,0) =-X0*CosAlpha + Y0*SinAlpha + X0;
      m(0,1) = SinAlpha;
      m(1,1) = CosAlpha;
      m(2,1) =-X0*SinAlpha - Y0*CosAlpha + Y0;	// Y0*(1-cos(alpha))-X0*sin(alpha);
      m(0,2)=0;  m(1,2)=0;  m(2,2)=1;
      }
    
    float_matrix corners(4,3,1.0);
    corners(0,0)=x;	corners(0,1)=y;
    corners(1,0)=x+dx;	corners(1,1)=y;
    corners(2,0)=x+dx;	corners(2,1)=y+dy;
    corners(3,0)=x;	corners(3,1)=y+dy;

    corners = m*corners;
    //printf("\nalpha=%f ",alpha);
    UpdateBBox(bbx, corners);
  }
}


/// Emit Postscript command for font selection.
/// @param[in,out]	Text	String builter that holds whole PS image.
/// @param[in]		FontSize  Size of font in [points]
/// @param[in]		FontName  Optional naome of font.
void Font2PS(string & Text, const float FontSize, const char *FontName)
{
  Text.cat_printf("/%s findfont", FontName);
  if(FontSize > 0.005)
    {
    if(FontSize>50 || (FontSize>1 && fabs(FontSize-(int)FontSize)<0.00999f))  // Do not request decimals for font larger than 50pt
	Text.cat_printf(" %d", (int)FontSize);
    else
        Text.cat_printf(" %.2f", FontSize);
    Text += " scalefont";
    }
  Text += " setfont";
}


/** Store color in PS notation. */
void Color2PS(string & Text, const RGB_Record & TextColor)
{
  Text.cat_printf("%0.2g %0.2g %0.2g setrgbcolor",
	         (float)TextColor.Red/256.0,
		 (float)TextColor.Green/256.0,
	         (float)TextColor.Blue/256.0);
}


PS_State::PS_State(void)
{
  PS_StateC *pPS_StateC = (PS_StateC *)this;
  memset(pPS_StateC,0,sizeof(PS_StateC));
  FillTransparency = 0xFF;
  HatchDistance = 1;
  memset(&PaperBackground, 0, sizeof(PaperBackground));
  memset(&FillBackground, 0xFF, sizeof(FillBackground)); //Use white paper.
  pPalette = NULL;
  pRaster = NULL;
  inPath = 0;
}


PS_State::PS_State(const PS_State &PSS)
{
  memcpy((PS_StateC *)this, (PS_StateC *)&PSS, sizeof(PS_StateC));
  FontName = PSS.FontName;
  PaperBackground = PSS.PaperBackground;
  pPalette = PSS.pPalette;
  if(pPalette!=NULL) InterlockedIncrement(&pPalette->UsageCount);
  pRaster = PSS.pRaster;
  if(pRaster!=NULL) InterlockedIncrement(&pRaster->UsageCount);
}


PS_State::~PS_State()
{
  if(pPalette != NULL)
  {
    if(InterlockedDecrement(&pPalette->UsageCount)<=0) delete pPalette;
    pPalette = NULL;
  }
  if(pRaster != NULL)
  {
    if(InterlockedDecrement(&pRaster->UsageCount)<=0) delete pRaster;
    pRaster = NULL;
  }
}


PS_State & PS_State::operator=(const PS_State & OrigPSS)
{
  memcpy((PS_StateC*)this, (PS_StateC*)&OrigPSS, sizeof(PS_StateC));
  PaperBackground = OrigPSS.PaperBackground;
  FontName = OrigPSS.FontName;
  AttachPalette(OrigPSS.pPalette);
  AttachRaster(OrigPSS.pRaster);
return *this;
}


void PS_State::AttachPalette(APalette *NewPalette)
{
  if(pPalette!=NULL)			// Detach previously attached raster
  {
    if(InterlockedDecrement(&pPalette->UsageCount)<=0) delete pPalette;
    pPalette = NULL;
  }
  if(NewPalette!=NULL)			// Attach raster now
  {
    pPalette=NewPalette; InterlockedIncrement(&pPalette->UsageCount);
  }
}


void PS_State::AttachRaster(Raster2DAbstract *NewRaster)
{
  if(pRaster!=NULL)			// Detach previously attached raster
  {
    if(InterlockedDecrement(&pRaster->UsageCount)<=0) delete pRaster;
    pRaster = NULL;
  }
  if(NewRaster!=NULL)			// Attach raster now
  {
    pRaster=NewRaster; InterlockedIncrement(&pRaster->UsageCount);
  }
}


////////////////////////////////////

vecPen::vecPen(void)
{
  LineStyle = 1;
  LineCap = 0;
  LineJoin = 0;
  PenWidth = -1;
}


void vecPen::prepExport(PS_State *PSS) const
{
  //printf("Selected vecPen %d %2.2X,%2.2X,%2.2X", LineStyle, LineColor.Red, LineColor.Green, LineColor.Blue);
  if(PSS)
  {
    if(memcmp(&LineColor,&PSS->LineColor,sizeof(RGB_Record)) != 0) 
    {
      memcpy(&PSS->LineColor,&LineColor,sizeof(RGB_Record));
      PSS->dirty |= PSS_LineColor;
    }
    if(PSS->LineStyle != LineStyle)
    {
      PSS->LineStyle = LineStyle;
      PSS->dirty |= PSS_LineStyle;
    }
    if(PSS->LineCap != LineCap)
    {
      PSS->LineCap = LineCap;
      PSS->dirty |= PSS_LineCap;
    }
    if(PSS->LineJoin != LineJoin)
    {
      PSS->LineJoin = LineJoin;
      PSS->dirty |= PSS_LineJoin;
    }
    if(PenWidth>=0 && PSS->LineWidth!=PenWidth)
    {      
      PSS->LineWidth = PenWidth;
      PSS->dirty |= PSS_LineWidth;
    }
  }
}


void vecPen::AttribFromPSS(const PS_State &PSS)
{
  memcpy(&LineColor, &PSS.LineColor, sizeof(PSS.LineColor));
  LineStyle = PSS.LineStyle;
  LineCap = PSS.LineCap;
  LineJoin = PSS.LineJoin;
  PenWidth = PSS.LineWidth;
}


vecBrush::vecBrush(const PS_State &PSS)
{
  BrushStyle = PSS.FillPattern;
  memcpy(&FillColor, &PSS.FillColor, sizeof(PSS.FillColor));
  memcpy(&FillBackground, &PSS.FillBackground, sizeof(FillBackground));
}


void vecBrush::prepExport(PS_State *PSS) const
{
  //printf("Selected vecBrush %d %2.2X,%2.2X,%2.2X", BrushStyle, FillColor.Red, FillColor.Green, FillColor.Blue);
  if(PSS)
  {
    if(memcmp(&FillColor,&PSS->FillColor,sizeof(RGB_Record)) != 0) 
    {
      memcpy(&PSS->FillColor,&FillColor,sizeof(RGB_Record));
      PSS->dirty |= PSS_FillColor;
    }
    if(BrushStyle != PSS->FillPattern)
        PSS->FillPattern = BrushStyle;
    //if(memcmp(&FillBackground, &PSS->FillBackground, sizeof(FillBackground))!=0)
    //{  // No need to compare.
      memcpy(&PSS->FillBackground, &FillBackground, sizeof(FillBackground));
    //}
  }
}


void vecBrush::AttribFromPSS(const PS_State &PSS)
{
  memcpy(&FillColor, &PSS.FillColor, sizeof(FillColor));
  memcpy(&FillBackground, &PSS.FillBackground, sizeof(FillBackground));
  BrushStyle = PSS.FillPattern;
}


void vecBrush::FeaturesEPS(uint32_t & Feature) const 
{
  switch(BrushStyle & 0x7F)
  {
    case FILL_SQUARES:	Feature|=EPS_FillBox1; break;
    case FILL_SQUARES2: Feature|=EPS_FillBox2; break;
    case FILL_PLUS:	Feature|=EPS_FillPlus; break;
    case FILL_BALLS:	Feature|=EPS_FillBalls; break;
    case FILL_TRIANGLES:Feature|=EPS_FillTriangle; break;
    case FILL_SM_SQUARES:Feature|=EPS_FillSmSquare; break;
    case FILL_BRICKS:   Feature|=EPS_FillBricks; break;
    case FILL_DIAG_BRICKS:Feature|=EPS_FillDiagBricks; break;
    case FILL_HONEYCOMB:Feature|=EPS_FillHoneycomb; break;
    case FILL_FISHSCALE:Feature|=EPS_FillFishscale; break;
    case FILL_ARCH:	Feature|=EPS_FillArch; break;
    case FILL_CHAINLINK:Feature|=EPS_FillChainlink; break;
    case FILL_PATIO:	Feature|=EPS_FillPatio; break;
    case FILL_WEAVE:	Feature|=EPS_FillWeave; break;
    case FILL_WAVES:	Feature|=EPS_FillWaves; break;
  }
}



vecFont::vecFont()
{
  ConvertCpg = NULL;
  Weight = 0;
  Italic = 0;
  FontOrientation10 = 0;
  memset(&TextColor,0,sizeof(TextColor));
}


vecFont::vecFont(const PS_State &PSS)
{
  ConvertCpg = PSS.ConvertCpg;
  FontSize = PSS.FontSize;
  FontSizeW = PSS.FontSizeW;
  Weight = PSS.FontWeight;
  Italic = PSS.FontItallic;
  FontOrientation10 = PSS.FontOrientation10;
  memcpy(&TextColor, &PSS.TextColor, sizeof(TextColor));
}


void vecFont::prepExport(PS_State *PSS) const
{
  if(PSS)
  {
    if(ConvertCpg!=PSS->ConvertCpg)
    {
      PSS->ConvertCpg = ConvertCpg;
    }
    if(FontSize!=PSS->FontSize)
    {
      PSS->FontSize = FontSize;
    }
    if(Weight != PSS->FontWeight)
    {
      PSS->FontWeight = Weight;
    }
    if(Italic != PSS->FontItallic)
    {
      PSS->FontItallic = Italic;
    }
    if(FontOrientation10 != PSS->FontOrientation10)
    {
      PSS->FontOrientation10 = FontOrientation10;
    }
  }
}


attrPalette::attrPalette(APalette *iniPalette)
{
  pPalette = iniPalette;
  if(pPalette!=NULL) InterlockedIncrement(&pPalette->UsageCount);
}


attrPalette::~attrPalette()
{
  if(pPalette != NULL)
  {
    if(InterlockedDecrement(&pPalette->UsageCount)<=0) delete pPalette;
    pPalette = NULL;
  }
}


void attrPalette::prepExport(PS_State *PSS) const
{
  if(PSS==NULL) return;
  PSS->AttachPalette(pPalette);
}


////////////////////////////////////


attrRaster::attrRaster(Raster2DAbstract *iniRaster, APalette *iniPalette)
{
  pRaster = iniRaster;
  if(pRaster!=NULL) InterlockedIncrement(&pRaster->UsageCount);
  pPalette = iniPalette;
  if(pPalette!=NULL) InterlockedIncrement(&pPalette->UsageCount);
}


attrRaster::~attrRaster()
{
  if(pRaster != NULL)
  {
    if(InterlockedDecrement(&pRaster->UsageCount)<=0) delete pRaster;
    pRaster = NULL;
  }
  if(pPalette != NULL)
  {
    if(InterlockedDecrement(&pPalette->UsageCount)<=0) delete pPalette;
    pPalette = NULL;
  }
}


void attrRaster::prepExport(PS_State *PSS) const
{
  if(PSS==NULL) return;
  PSS->AttachRaster(pRaster);
}


////////////////////////////////////

vecTransform::vecTransform(void)
{
  CenterX = CenterY = TranslateX = TranslateY = 0;
  RotAngle = 0;
  ScaleX = ScaleY = 1;
}


bool vecTransform::ApplyTransform(string &s)
{
bool ContextSaved = false;

  if(fabs(RotAngle) > 1e-10)
  {
    s.cat_printf("\ngsave\n%2.2f %2.2f translate\n%2.2f rotate\n%2.2f %2.2f translate",
		 CenterX,CenterY, RotAngle, -CenterX,-CenterY);
    ContextSaved = true;
  }

  if(fabs(TranslateX)>1e-10 || fabs(TranslateY)>1e-10)
  {
    if(!ContextSaved)
    {
      s += "\ngsave";
      ContextSaved = true;
    }
    s.cat_printf("\n%2.2f %2.2f translate", TranslateX, TranslateY);
  }

  if(fabs(1-ScaleX)>1e-3 || fabs(1-ScaleY)>1e-3)
  {
    if(!ContextSaved)
    {
      s += "\ngsave";
      ContextSaved = true;
    }
    s.cat_printf("\n%2.2f %2.2f scale", ScaleX, ScaleY);
  }

  return ContextSaved;
}


////////////////////////////////////


void VectorObject::Export2EPS(FILE *F, PS_State *PSS) const
{
  temp_string StrTmp(Export2EPS(PSS));
  if(StrTmp.length() > 0)
      fputs(StrTmp(), F);
}


////////////////////////////////////


VectorEllipse::VectorEllipse(float iniBottomRect, float iniTopRect, float iniRightRect, float iniLeftRect)
{
 Tx = NULL;
 BottomRect = iniBottomRect;
 TopRect = iniTopRect;
 RightRect = iniRightRect;
 LeftRect = iniLeftRect; 
 bAngle = 0;
 eAngle = 360;
}


VectorEllipse::VectorEllipse(const VectorEllipse & VE): vecPen(VE), vecBrush(VE)
{
 Tx = NULL;
 BottomRect = VE.BottomRect;
 TopRect = VE.TopRect;
 RightRect = VE.RightRect;
 LeftRect = VE.LeftRect; 
 bAngle = VE.bAngle;
 eAngle = VE.eAngle;
}


VectorEllipse::~VectorEllipse()
{
  if(Tx)
      {delete(Tx); Tx=NULL;}
}


VectorObject *VectorEllipse::Duplicate(void) const
{
  return new VectorEllipse(*this);
}


temp_string VectorEllipse::Export2EPS(PS_State *PSS) const
{
string str;

  if(PSS != NULL)
  {
    vecPen::prepExport(PSS);
    vecBrush::prepExport(PSS);
    PS_Attr(str,PSS);
  }

  bool ContextSaved = false;
  if(Tx) 
    ContextSaved = Tx->ApplyTransform(str);

  str.cat_printf("\nnewpath"
                 "\n%2.2f %2.2f %2.2f %2.2f %d %d DrawEllipse",
			 (RightRect+LeftRect)/2, (TopRect+BottomRect)/2,
			 fabs(RightRect-LeftRect)/2, fabs(TopRect-BottomRect)/2,
			 bAngle, eAngle);

  FillObjectPS(LeftRect,RightRect,BottomRect,TopRect,str,PSS);
  if(ContextSaved) str += "\ngrestore";

return temp_string(str);
}

void VectorEllipse::Transform(const AbstractTransformXY &Tx)
{
  Tx.ApplyTransform(LeftRect, BottomRect);
  Tx.ApplyTransform(RightRect, TopRect);

  if(TopRect < BottomRect)
  {
    const float f = TopRect;
    TopRect = BottomRect;
    BottomRect = f;
  }
  if(RightRect < LeftRect)
  {
    const float f = RightRect;
    RightRect = LeftRect;
    LeftRect = f;
  }

  if(this->Tx!=NULL)
    Tx.ApplyTransform(this->Tx->CenterX, this->Tx->CenterY);
}


VectorObject *VectorPie::Duplicate(void) const
{
  VectorEllipse *pVP = new VectorPie(BottomRect,TopRect,RightRect,LeftRect);
  *(vecPen*)pVP = *(vecPen*)this;
  *(vecBrush*)pVP = *(vecBrush*)this;
return pVP;
}


temp_string VectorPie::Export2EPS(PS_State *PSS) const
{
string str;

  if(PSS != NULL)
  {
    vecPen::prepExport(PSS);
    vecBrush::prepExport(PSS);
    PS_Attr(str,PSS);
  }

  bool ContextSaved = false;
  if(Tx) 
    ContextSaved = Tx->ApplyTransform(str);

  str.cat_printf("\nnewpath"
		 "\n%2.2f %2.2f moveto", (RightRect+LeftRect)/2, (TopRect+BottomRect)/2);
  str.cat_printf("\n%2.2f %2.2f %2.2f %2.2f %d %d DrawEllipse"
	         "\nclosepath",
			 (RightRect+LeftRect)/2, (TopRect+BottomRect)/2,
			 fabs(RightRect-LeftRect)/2, fabs(TopRect-BottomRect)/2,
			 bAngle, eAngle);
  //str.cat_printf("\n%2.2f %2.2f lineto", (RightRect+LeftRect)/2, (TopRect+BottomRect)/2);

  FillObjectPS(LeftRect,RightRect,BottomRect,TopRect,str,PSS);

  if(ContextSaved) str += "\ngrestore";

return temp_string(str);
}



/////////////////////////////////////////////////////////////


VectorRectangle::VectorRectangle(float iniBottomRect, float iniTopRect, float iniLeftRect, float iniRightRect)
{
 Tx = NULL;
 BottomRect = iniBottomRect;
 TopRect = iniTopRect;
 RightRect = iniRightRect;
 LeftRect = iniLeftRect;
}


VectorRectangle::~VectorRectangle()
{
  if(Tx)
      {delete(Tx); Tx=NULL;}
}


VectorObject *VectorRectangle::Duplicate(void) const
{
  VectorRectangle *pVR = new VectorRectangle(BottomRect,TopRect,RightRect,LeftRect);
  *(vecPen*)pVR = *(vecPen*)this;
  *(vecBrush*)pVR = *(vecBrush*)this;
return pVR;
}


unsigned VectorRectangle::isInside(float xp, float yp) const
{
  if(BrushStyle==FILL_NONE) return 0;
  if(xp>=LeftRect && xp<=RightRect && yp>=BottomRect && xp<=TopRect)
  {
    if(xp>LeftRect && xp<RightRect && yp>BottomRect && xp<TopRect) return 2;
    return 1;
  }
  return 0;
}


void VectorRectangle::CalcBoundingBox(FloatBBox &bbx) const
{
float rx, ry;

  rx=LeftRect;		ry=BottomRect;
  if(Tx) Tx->ApplyTransform(rx,ry);
  UpdateBBox(bbx, 0, rx-PenWidth/2, ry-PenWidth/2, PenWidth, PenWidth);

  rx=RightRect;		ry=BottomRect;
  if(Tx) Tx->ApplyTransform(rx,ry);
  UpdateBBox(bbx, 0, rx-PenWidth/2, ry-PenWidth/2, PenWidth, PenWidth);

  rx=RightRect;		ry=TopRect;
  if(Tx) Tx->ApplyTransform(rx,ry);
  UpdateBBox(bbx, 0, rx-PenWidth/2, ry-PenWidth/2, PenWidth, PenWidth);

  rx=LeftRect;		ry=TopRect;
  if(Tx) Tx->ApplyTransform(rx,ry);
  UpdateBBox(bbx, 0, rx-PenWidth/2, ry-PenWidth/2, PenWidth, PenWidth);
}


temp_string VectorRectangle::Export2EPS(PS_State *PSS) const
{
string str;
float x, y;

  if(PSS != NULL)
  {
    vecBrush::prepExport(PSS);
    vecPen::prepExport(PSS);
    PS_Attr(str,PSS);
  }

  x=LeftRect; y=BottomRect;
  if(Tx) Tx->ApplyTransform(x,y);
  str.cat_printf("\nnewpath\n%2.2f %2.2f moveto", x, y);
  x=LeftRect; y=TopRect;
  if(Tx) Tx->ApplyTransform(x,y);
  str.cat_printf("\n%2.2f %2.2f lineto", x, y);
  x=RightRect; y=TopRect;
  if(Tx) Tx->ApplyTransform(x,y);
  str.cat_printf("\n%2.2f %2.2f lineto", x, y);
  x=RightRect; y=BottomRect;
  if(Tx) Tx->ApplyTransform(x,y);
  str.cat_printf("\n%2.2f %2.2f lineto", x, y);
  str.cat_printf("\nclosepath");

  FillObjectPS(LeftRect,RightRect,BottomRect,TopRect,str,PSS);

return temp_string(str);
}


void VectorRectangle::Transform(const AbstractTransformXY &NewTx)
{
  if(Tx)	// Tx must be flushed, transformations are not comutative.
  {
    Tx->ApplyTransform(LeftRect, BottomRect);
    Tx->ApplyTransform(RightRect, TopRect);
    delete Tx;
    Tx = NULL;
  }
  NewTx.ApplyTransform(LeftRect, BottomRect);
  NewTx.ApplyTransform(RightRect, TopRect);

  if(TopRect < BottomRect)
  {
    const float f = TopRect;
    TopRect = BottomRect;
    BottomRect = f;
  }
  if(RightRect < LeftRect)
  {
    const float f = RightRect;
    RightRect = LeftRect;
    LeftRect = f;
  }
}


#ifdef _DEBUG
void VectorRectangle::Dump2File(FILE *F) const
{
  fprintf(F,"VectorRectangle{Left=%2.2f; Right=%2.2f; Bottom=%2.2f; Top=%2.2f}\n", 
	  LeftRect, RightRect, BottomRect, TopRect);
}
#endif



/////////////////////////////////////////////////////////////

VectorRectangleArc::VectorRectangleArc(float iniBottomRect, float iniTopRect, 
                    float iniRightRect, float iniLeftRect, float iniHradius, float iniVradius)
                   : VectorRectangle(iniBottomRect,iniTopRect,iniRightRect,iniLeftRect)
{
  Hradius = iniHradius;
  Vradius = iniVradius;
}


VectorObject *VectorRectangleArc::Duplicate(void) const
{
  VectorRectangleArc *pVRA = new VectorRectangleArc(BottomRect,TopRect,RightRect,LeftRect,Hradius,Vradius);
  *(vecPen*)pVRA = *(vecPen*)this;
  *(vecBrush*)pVRA = *(vecBrush*)this;
return pVRA;
}


void VectorRectangleArc::Transform(const AbstractTransformXY &Tx)
{
float Hr1,Hr2, Vr1,Vr2, Hr3,Vr3;
  Hr3=Hr1 = LeftRect;	Hr2 = LeftRect + Hradius;
  Vr3=Vr1 = BottomRect;	Vr2 = BottomRect + Vradius;

  VectorRectangle::Transform(Tx);  
  Tx.ApplyTransform(Hr2, Vr1);			// [LeftRect+Hradius; BottomRect]
  Tx.ApplyTransform(Hr1, Vr2);
  Tx.ApplyTransform(Hr3, Vr3);			// Bottom and top rect could be realligned, must do a separate transform.

  Hradius = sqrt(sqr(Hr3-Hr2) + sqr(Vr3-Vr1));
  Vradius = sqrt(sqr(Hr3-Hr1) + sqr(Vr3-Vr2));
  //if(Hradius<0) Hradius=-Hradius;		-- no need square root is always positive
  //if(Vradius<0) Vradius=-Vradius;
}


temp_string VectorRectangleArc::Export2EPS(PS_State *PSS) const
{
bool ContextSaved = false;

  if(fabs(Hradius)<1e-5 || fabs(Vradius)<1e-5 || Tx!=NULL)
      return VectorRectangle::Export2EPS(PSS);

  string PSData;

  if(PSS != NULL)
  {
    vecBrush::prepExport(PSS);
    vecPen::prepExport(PSS);
    PS_Attr(PSData, PSS);
  }

  //if(Tx) 
  //  ContextSaved = Tx->ApplyTransform(PSData);
  
  float HRadiusFix = Hradius;
  float VRadiusFix = Vradius;

  float Y_ur = fabs(RightRect-LeftRect)/2;
  if(Hradius > Y_ur) HRadiusFix=Y_ur;
  Y_ur = fabs(TopRect-BottomRect)/2;
  if(Vradius > Y_ur) VRadiusFix=Y_ur;

  float Y_ll = BottomRect;
  Y_ur = TopRect;

  if(fabs(HRadiusFix-VRadiusFix) > 1e-5)
  {	//Horizontal and Vertical radiuses are different - scale canvas
    const float Ty = VRadiusFix/HRadiusFix;
    if(!ContextSaved) PSData+="\ngsave";
    PSData.cat_printf("\n0 %2.2f translate\n1 %2.3f scale", Y_ll, Ty);
    Y_ur -= Y_ll;
    Y_ll = 0;
    Y_ur /= Ty;
    ContextSaved = true;
 }

 PSData += "\nnewpath";
 PSData.cat_printf("\n%2.2f %2.2f moveto", RightRect-HRadiusFix, Y_ll);
 PSData.cat_printf("\n%2.2f %2.2f %2.2f 270 0 arc", RightRect-HRadiusFix, Y_ll+HRadiusFix, HRadiusFix);
 PSData.cat_printf("\n%2.2f %2.2f %2.2f 0 90 arc", RightRect-HRadiusFix, Y_ur-HRadiusFix, HRadiusFix);
 PSData.cat_printf("\n%2.2f %2.2f %2.2f 90 180 arc", LeftRect+HRadiusFix, Y_ur-HRadiusFix, HRadiusFix);
 PSData.cat_printf("\n%2.2f %2.2f %2.2f 180 270 arc", LeftRect+HRadiusFix, Y_ll+HRadiusFix, HRadiusFix);

 PSData+="\nclosepath";
 FillObjectPS(RightRect, LeftRect, Y_ll, Y_ur,PSData,PSS);
 if(ContextSaved) PSData+="\ngrestore";
 return temp_string(PSData);
}


#ifdef _DEBUG
void VectorRectangleArc::Dump2File(FILE *F) const
{
  fprintf(F,"VectorRectangleArc{Left=%2.2f; Right=%2.2f; Bottom=%2.2f; Top=%2.2f; Hradius=%2.2f; Vradius=%2.2f}\n", 
	  LeftRect, RightRect, BottomRect, TopRect, Hradius, Vradius);
}
#endif



/////////////////////////////////////////////////////////////


temp_string VectorPolygon::Export2EPS(PS_State *PSS) const
{
string str;
  if(Points==NULL || CountPoints<=0) return temp_string();

  if(PSS != NULL)
  {
    if(PSS->inPath <=0)
    {
      vecBrush::prepExport(PSS);
      vecPen::prepExport(PSS);
      PS_Attr(str,PSS);
/* This is not working. Separate polygons are not clipping.
      if(PSS->PolyFillMode==1 && UpperCont!=NULL && CountPoints>=3)
      {
        unsigned IntersectCount=0;
        const VectorObject *vec = UpperCont->FirstObject;
        while(vec!=NULL)
        {
          if(vec==this) break;
          IntersectCount += vec->isInside(Points[0],Points[1]);
          if(vec==UpperCont->LastObject) break;
          vec = vec->NextObject;
        }
        if((IntersectCount & 3) == 2)
        {
          PSS->FirstTimeFix = true;	// Flip foreground to background.
        }
      }
*/
    }
  }

  if(LineStyle==0 && BrushStyle==0)	// No line and no fill means invisible object,
  {
    return 
#ifdef _DEBUG
	"\n%%Polygon skipped because it is not visible.\n";
#else
	"";			// Skip it.   
#endif
  }
  
  const float *pEnd = Points + 2*CountPoints;
  float MinPolyX=65537,MaxPolyX=-32767,MinPolyY=65537,MaxPolyY=-32767;

  for(float *p=Points; p<pEnd; p+=2)
  {
    const float x = *p;
    const float y = p[1];
    if(x<MinPolyX) MinPolyX=x;
    if(x>MaxPolyX) MaxPolyX=x;
    if(y<MinPolyY) MinPolyY=y;
    if(y>MaxPolyY) MaxPolyY=y;

    if(p==Points && PSS!=NULL && PSS->inPath>=2) continue;
    str.cat_printf(p==Points ? "\nnewpath\n%2.2f %2.2f moveto" : "\n%2.2f %2.2f lineto",
		    x, y);
  }

  if(PSS==NULL || PSS->inPath<=0)
  {
    if(Close) str += "\nclosepath";
  
    FillObjectPS(MinPolyX,MaxPolyX,MinPolyY,MaxPolyY,str,PSS);
  }
  return temp_string(str);
}


#ifdef _DEBUG
void VectorPolygon::Dump2File(FILE *F) const
{
const float *pEnd = Points + 2*CountPoints;
  fprintf(F,"VectorPolygon{Brush=%2.2X,%2.2X,%2.2X",FillColor.Red,FillColor.Green,FillColor.Blue);
  for(float *p=Points; p<pEnd; p+=2)
  {
    fprintf(F,"[%2.2f;%2.2f]", p[0], p[1]);
  }
  fputs("}\n",F);
}
#endif


/*unsigned VectorPolygon::isInside(float xp, float yp) const
{
  unsigned isX = isInsideX(xp,yp);
  unsigned isY = isInsideY(xp,yp);

  if(isX == isY) return isX; 
  
FILE *F;
F=fopen("R:\\dump.csv","wb");
const float *pEnd = Points + 2*(CountPoints);
for(float *p=Points; p<pEnd; p+=2)
{
  fprintf(F,"%f,%f\n", p[0], p[1]);
}
fclose(F);

  if(isX < isY) return isX;	// Trouble detected here.
  return isY;
}*/


unsigned VectorPolygon::isInside(float xp, float yp) const
{
unsigned Intersects = 0;
bool TouchLine = false;
int OriUpDn = 0;

  if(CountPoints<=2 || Points==NULL || !Close || BrushStyle==FILL_NONE) return 0;

  const float *pEnd = Points + 2*(CountPoints-1);
  for(float *p=Points; p<pEnd; p+=2)
  {
    if(fabs(p[1]-p[3]) < 1e-5)			// x axis segment; same y
    {
      if(fabs(p[1]-yp) < 1e-5)			// crossing the half-line
      {
        if(p[0]>xp || p[2]>xp) continue;
        if(OriUpDn==0)
        {
          if(fabs(Points[1]-pEnd[1]) >= 1e-5)
            OriUpDn = (Points[1]>pEnd[1]) ? 1 : -1;
          else
          {
            for(int idx=2*(CountPoints-2)+1; idx>2; idx-=2)
            {
              if(fabs(Points[idx]-Points[idx+2]) >= 1e-5)
              {
                OriUpDn = (Points[idx+2]>Points[idx]) ? 1 : -1;
                break;
              }
            }
          }
          TouchLine = true;
        }
      }
      continue;
    }

    const float t = (yp-p[1]) / (p[3]-p[1]);
    const float xl = p[0] + (p[2]-p[0])*t;
    if(xl > xp) {TouchLine=false;continue;}	// No intersection with half-line

    if(fabs(yp-p[1])<1e-5 || fabs(yp-p[3])<1e-5) // Node intersection.
    {
      Intersects++;
      if(TouchLine)
      {
        int NewOri = (p[3]>p[1]) ? 1 : -1;
        if(NewOri!=OriUpDn)			// Up & Down touch
          Intersects -= 2;			// This is NOT exit from an area.
        TouchLine = false;
      }
      else
        TouchLine = true;

      OriUpDn = (p[3]>p[1]) ? 1 : -1;
      continue;
    }

    TouchLine = false;
    if(t>1 || t<0) continue;			// No intersection with line segment.
    Intersects += 2;
  }

  if(fabs(pEnd[1]-Points[1]) >= 1e-5)	 	// No x axis segment
  {
    const float t = (yp-pEnd[1]) / (pEnd[1]-Points[1]);
    const float xl = pEnd[0] + t*(Points[0]-pEnd[0]);
    if(xl < xp)
    {
      if(fabs(yp-pEnd[1])<1e-5 || fabs(yp-Points[1])<1e-5) // Node intersection.
      {
        Intersects++;
      }
      else
      {
        if(t<=1 && t>=0) 
		Intersects += 2;
      }
    }
  }

  return Intersects;
}


/*unsigned VectorPolygon::isInsideY(float xp, float yp) const
{
unsigned Intersects = 0;
bool TouchLine = false;
int OriUpDn = 0;

  if(CountPoints<=2 || Points==NULL || !Close || BrushStyle==FILL_NONE) return 0;

  const float *pEnd = Points + 2*(CountPoints-1);
  for(float *p=Points; p<pEnd; p+=2)
  {
    if(fabs(p[0]-p[2]) < 1e-5)			// y axis segment; same x
    {
      if(fabs(p[0]-xp) < 1e-5)			// crossing the half-line
      {
        if(p[0]>xp || p[2]>xp) continue;
        if(OriUpDn==0)
        {
          if(fabs(Points[0]-pEnd[0]) >= 1e-5)
            OriUpDn = (Points[0]>pEnd[0]) ? 1 : -1;
          else
          {
            for(int idx=2*(CountPoints-2); idx>2; idx-=2)
            {
              if(fabs(Points[idx]-Points[idx+2]) >= 1e-5)
              {
                OriUpDn = (Points[idx+2]>Points[idx]) ? 1 : -1;
                break;
              }
            }
          }
          TouchLine = true;
        }        
      }      
      continue;
    }    

    const float t = (xp-p[0]) / (p[2]-p[0]);
    const float yl = p[1] + (p[3]-p[1])*t;
    if(yl > yp) {TouchLine=false;continue;}	// No intersection with half-line

    if(fabs(xp-p[0])<1e-5 || fabs(xp-p[2])<1e-5) // Node intersection.
    {
      Intersects++;
      if(TouchLine)
      {
        int NewOri = (p[2]>p[0]) ? 1 : -1;
        if(NewOri!=OriUpDn)			// Up & Down touch
          Intersects -= 2;			// This is NOT exit from an area.
        TouchLine = false;
      }
      else
        TouchLine = true;

      OriUpDn = (p[2]>p[0]) ? 1 : -1;
      continue;
    }

    TouchLine = false;
    if(t>1 || t<0) continue;			// No intersection with line segment.
    Intersects += 2;
  }

  if(fabs(pEnd[0]-Points[0]) >= 1e-5)	 	// No x axis segment
  {
    const float t = (xp-pEnd[0]) / (pEnd[0]-Points[0]);
    const float yl = pEnd[1] + t*(Points[1]-pEnd[1]);
    if(yl < yp)
    {
      if(fabs(xp-pEnd[0])<1e-5 || fabs(xp-Points[0])<1e-5) // Node intersection.
      {
        Intersects++;
      }
      else
      {
        if(t<=1 && t>=0) Intersects += 2;
      }
    }
  }

  return Intersects;
}
*/



/////////////////////////////////////////////////////////////


void VectorCurve::CalcBoundingBox(FloatBBox &bbx) const
{
  if(Points==NULL || CountPoints<=0) return;

  const float *pEnd = Points + 2*CountPoints;
  for(float *p=Points; p<pEnd; p+=6)
  {
    UpdateBBox(bbx,0,p[0], p[1], 0,0);
  }
}


temp_string VectorCurve::Export2EPS(PS_State *PSS) const
{
string str;
float MinPolyX,MaxPolyX,MinPolyY,MaxPolyY;
bool ExtraStartup;
float *p = Points;

	// There must be at least two points for the line.
  if(Points==NULL || CountPoints<=1) return temp_string();

  if((CountPoints-1)%3 == 0)
  {
    ExtraStartup = true;
  }
  else
  {
    if((CountPoints)%3!=0 || CountPoints<6) return VectorLine::Export2EPS(PSS);
    ExtraStartup = false;
    p += 2;		// point to anchor points.
  }

  if(PSS!=NULL)
  {
    if(PSS->inPath<=0)
    {
      vecPen::prepExport(PSS);
      if(/*Filled &&*/ BrushStyle!=FILL_NONE)
          vecBrush::prepExport(PSS);
      PS_Attr(str,PSS);
    }
    if(PSS->inPath>1) goto SkipNewPath;
  }
  
  str.cat_printf("\nnewpath\n%2.2f %2.2f moveto", *p, p[1]);
SkipNewPath:
  MinPolyX = MaxPolyX = *p;	// Control point
  MinPolyY = MaxPolyY = p[1];

  const float *pEnd = Points + 2*CountPoints;
  if(ExtraStartup)
  {
    for(p=Points+2; p<pEnd; p+=6)
    {
      str.cat_printf("\n%2.2f %2.2f %2.2f %2.2f %2.2f %2.2f curveto", *p, p[1], p[2], p[3], p[4], p[5]);

      if(*p<MinPolyX) MinPolyX=*p;	if(*p>MaxPolyX) MaxPolyX=*p;
      if(p[1]<MinPolyY) MinPolyY=p[1];	if(p[1]>MaxPolyY) MaxPolyY=p[1];

      if(p[2]<MinPolyX) MinPolyX=p[2];	if(p[2]>MaxPolyX) MaxPolyX=p[2];
      if(p[3]<MinPolyY) MinPolyY=p[3];	if(p[3]>MaxPolyY) MaxPolyY=p[3];

      if(p[4]<MinPolyX) MinPolyX=p[4];	if(p[4]>MaxPolyX) MaxPolyX=p[4];
      if(p[5]<MinPolyY) MinPolyY=p[5];	if(p[5]>MaxPolyY) MaxPolyY=p[5];
    }
  }
  else
  {
    float xt_old = Points[4];		// Next control point.
    float yt_old = Points[5];
    for(p=Points+6; p<pEnd; p+=6)
    {
      str.cat_printf("\n%2.2f %2.2f %2.2f %2.2f %2.2f %2.2f curveto", xt_old,yt_old, *p, p[1], p[2], p[3]);

      if(*p<MinPolyX) MinPolyX=*p;	if(*p>MaxPolyX) MaxPolyX=*p;
      if(p[1]<MinPolyY) MinPolyY=p[1];	if(p[1]>MaxPolyY) MaxPolyY=p[1];

      if(p[2]<MinPolyX) MinPolyX=p[2];	if(p[2]>MaxPolyX) MaxPolyX=p[2];
      if(p[3]<MinPolyY) MinPolyY=p[3];	if(p[3]>MaxPolyY) MaxPolyY=p[3];

      xt_old = p[4];
      yt_old = p[5];  
      if(xt_old<MinPolyX) MinPolyX=xt_old;	if(xt_old>MaxPolyX) MaxPolyX=xt_old;
      if(yt_old<MinPolyY) MinPolyY=yt_old;	if(yt_old>MaxPolyY) MaxPolyY=yt_old;
    }
  }

  if(PSS==NULL || PSS->inPath<=0)
  {
    if(/*Filled &&*/ BrushStyle!=FILL_NONE)
    {
      str += "\nclosepath";
      FillObjectPS(MinPolyX,MaxPolyX,MinPolyY,MaxPolyY,str,PSS);
    }
    else
    {
      if(Close) str += "\nclosepath";
      str += "\nstroke"; 
    }
  }
  return temp_string(str);
}


/////////////////////////////////////////////////////////////

VectorLine::VectorLine(int iniPointCount)
{
  Close = false;
  if(iniPointCount<=0)
  {
    Points = NULL;
    CountPoints = 0;
    return;
  }
  Points = (float*)malloc(sizeof(float)*iniPointCount*2);
  CountPoints = iniPointCount;
}


VectorLine::VectorLine(float *iniPoints, int iniPointCount)
{
  Points = iniPoints;
  CountPoints = iniPointCount;
  Close = false;
}


VectorLine::VectorLine(const VectorLine &VL): vecPen(VL)
{
  if(VL.CountPoints>0 && VL.Points!=NULL)
  {
    Points = (float*)malloc(sizeof(float)*VL.CountPoints*2);
    if(Points)
    {
      CountPoints = VL.CountPoints;
      memcpy(Points, VL.Points, sizeof(float)*CountPoints*2);
    }
    else
      CountPoints = 0;
  }
  else
  {
    CountPoints = 0;
    Points = NULL;
  }
}


VectorLine::~VectorLine()
{
  if(Points!=NULL)
  {
    free(Points);
    Points = NULL;
  }  
  CountPoints = 0;
}


void VectorLine::CalcBoundingBox(FloatBBox &bbx) const
{
  if(Points==NULL || CountPoints<=0) return;

  const float *pEnd = Points + 2*CountPoints;
  for(float *p=Points; p<pEnd; p+=2)
  {
    UpdateBBox(bbx,0,p[0], p[1], 0,0);
  }
}


VectorObject *VectorLine::Duplicate(void) const
{
  return new VectorLine(*this);
}


temp_string VectorLine::Export2EPS(PS_State *PSS) const
{
string str;
	// There must be at least two points for the line.
  if(Points==NULL || CountPoints<=1) return temp_string();

  if(PSS!=NULL)
  {
    if(PSS->inPath <= 0)
    {
      vecPen::prepExport(PSS);
      PS_Attr(str,PSS);
    }
    else
      if(PSS->inPath >= 2) goto SkipMoveTo;
  }

  str.cat_printf("\nnewpath\n%2.2f %2.2f moveto", *Points, Points[1]);
SkipMoveTo:

  float *p;
  const float *pEnd = Points + 2*CountPoints;
  for(p=Points+2; p<pEnd; p+=2)
  {
    str.cat_printf("\n%2.2f %2.2f lineto", *p, p[1]);
  }

  if(PSS==NULL || PSS->inPath <=0)
  {
    if(Close) str += "\nclosepath";
    str += "\nstroke";
  }
  return temp_string(str);
}


#ifdef _DEBUG
void VectorLine::Dump2File(FILE *F) const
{
const float *pEnd = Points + 2*CountPoints;
  fputs("VectorLine{",F);
  for(float *p=Points; p<pEnd; p+=2)
  {
    fprintf(F,"[%2.2f;%2.2f]", p[0], p[1]);
  }
  fputs("}\n",F);
}
#endif


void VectorLine::Transform(const AbstractTransformXY &Tx)
{
  if(Points==NULL || CountPoints<=0) return;

  const float *pEnd = Points + 2*CountPoints;
  for(float *p=Points; p<pEnd; p+=2)
  {  
    Tx.ApplyTransform(p[0], p[1]);
  }
}


/////////////////////////////////////////////////////////////


void PsBlob::FeaturesEPS(uint32_t & Feature) const
{
  if(Blob==NULL) return;
  if(StrStr(Blob,"DrawEllipse")!=NULL) Feature |= EPS_DrawElipse;
  if(StrStr(Blob,"accentshow")!=NULL)  Feature |= EPS_accentshow;
  if(StrStr(Blob,"ACCENTSHOW")!=NULL)  Feature |= EPS_ACCENTSHOW;  
}


VectorObject *PsBlob::Duplicate(void) const
{
  return new PsBlob(strdup(Blob));
}


/////////////////////////////////////////////////////////////


VectorList::VectorList(void)
{
  FirstObject = LastObject = NULL;
  VectorObjects = 0;
}


VectorList::~VectorList()
{
VectorObject *IterObject = FirstObject;

  while(IterObject!=NULL)
  {
    VectorObject *ErasedObject = IterObject;
    IterObject = IterObject->NextObject;
    delete(ErasedObject);
  }
  
  FirstObject = LastObject = NULL;
  VectorObjects = 0;
}


void VectorList::CalcBoundingBox(FloatBBox &bbx) const
{
VectorObject *IterObject = FirstObject;
  while(IterObject!=NULL)
  {
    IterObject->CalcBoundingBox(bbx);
    IterObject = IterObject->NextObject;
  }
}


VectorObject *VectorList::Duplicate(void) const
{
  VectorList *pVL = new VectorList();
  VectorObject *ItObjs = FirstObject;
  while(ItObjs!=NULL)
  {
    pVL->AddObject(ItObjs->Duplicate());
    ItObjs = ItObjs->NextObject;
  }
return pVL;
}


temp_string VectorList::Export2EPS(PS_State *PSS) const
{
string str;
VectorObject *IterObject = FirstObject;

  while(IterObject!=NULL)
  {
    str += IterObject->Export2EPS(PSS);
    IterObject = IterObject->NextObject;
  }
  return temp_string(str);
}


#ifdef _DEBUG
void VectorList::Dump2File(FILE *F) const
{
VectorObject *IterObject = FirstObject;

  while(IterObject!=NULL)
  {
    IterObject->Dump2File(F);
    IterObject = IterObject->NextObject;
  }
}
#endif


void VectorList::FeaturesEPS(uint32_t & Feature) const
{
VectorObject *IterObject = FirstObject;

  while(IterObject!=NULL)
  {
    IterObject->FeaturesEPS(Feature);
    IterObject = IterObject->NextObject;
  }

}


void VectorList::Transform(const AbstractTransformXY &Tx)
{
VectorObject *IterObject = FirstObject;
  while(IterObject!=NULL)
  {
    IterObject->Transform(Tx);
    IterObject = IterObject->NextObject;
  }
}


void VectorList::AddObject(VectorObject *NewObj)
{
  if(NewObj==NULL || NewObj==this) return;

  NewObj->UpperCont = this;
  if(FirstObject==NULL)
  {
    FirstObject = LastObject = NewObj;
  }
  else
  {
   LastObject->NextObject = NewObj;
   LastObject = NewObj;
  }
  VectorObjects++;
}


void VectorList::Append(VectorList &VectList)
{
  if(VectList.FirstObject==NULL || VectList.VectorObjects==0) return;	// listto append is empty.
  if(FirstObject==NULL)
  {
    FirstObject = VectList.FirstObject;
    LastObject = VectList.LastObject;
    VectorObjects = VectList.VectorObjects;
  }
  else
  {
    LastObject->NextObject = VectList.FirstObject;
    LastObject = VectList.LastObject;
    VectorObjects += VectList.VectorObjects;
  }
  VectList.FirstObject = VectList.LastObject = NULL;
  VectList.VectorObjects = 0;
}


void VectorList::Export2EPS(FILE *F, PS_State *PSS) const
{
VectorObject *IterObject = FirstObject;
  if(F==NULL) return;
  while(IterObject!=NULL)
  {
    IterObject->Export2EPS(F,PSS);
    IterObject = IterObject->NextObject;
  }
}


void VectorList::Swap(VectorList &VectList)
{
const int count = VectorObjects;
  VectorObjects = VectList.VectorObjects;
  VectList.VectorObjects = count;

  VectorObject *pObj = FirstObject;
  FirstObject = VectList.FirstObject;
  VectList.FirstObject = pObj;

  pObj = LastObject;
  LastObject = VectList.LastObject;
  VectList.LastObject = pObj;
}


bool VectorList::ReplaceObject(VectorObject *OrigObj, VectorObject *NewObj)
{
VectorObject *IterObject;
  if(OrigObj==NULL || OrigObj==NewObj) return false;
  if(FirstObject==NULL) return false;

  if(FirstObject==OrigObj)
  {
    FirstObject = NewObj;
    NewObj->NextObject = OrigObj->NextObject;
    if(LastObject==OrigObj) LastObject=NewObj;
    return true;
  }

  IterObject = FirstObject;
  while(IterObject->NextObject!=NULL)
  {
    if(IterObject->NextObject==OrigObj)
    {
      IterObject->NextObject = NewObj;
      NewObj->NextObject = OrigObj->NextObject;
      if(LastObject==OrigObj) LastObject=NewObj;
      return true;
    }
    if(IterObject->NextObject->ReplaceObject(OrigObj,NewObj))
    {
      return true;
    }
    IterObject = IterObject->NextObject;
  }
return false;
}


/////////////////////////////////////////////////////////////


temp_string VectorPath::Export2EPS(PS_State *PSS) const
{
string str;
VectorObject *IterObject = FirstObject;

  if(IterObject==NULL) return("");
  if(PSS)
  {
    vecBrush::prepExport(PSS);
    vecPen::prepExport(PSS);
    PS_Attr(str,PSS);
    PSS->inPath = 1;
  }
  while(IterObject!=NULL)
  {
    str += IterObject->Export2EPS(PSS);
    IterObject = IterObject->NextObject;
    if(PSS) PSS->inPath = 2;
  }
  if(PSS)
  {
    if(Close) str += "\nclosepath";
    if(PSS->FillPattern==FILL_NONE)
      str += "\nstroke";
    else
    {
      FloatBBox bbx;
      CalcBoundingBox(bbx);
      InitBBox(bbx);
      FillObjectPS(bbx.MinX,bbx.MaxX,bbx.MinY,bbx.MaxY,str,PSS);
    }
    PSS->inPath = 0;
  }
  return temp_string(str);
}


void VectorPath::Export2EPS(FILE *F, PS_State *PSS) const
{
VectorObject *IterObject = FirstObject;
  if(F==NULL || IterObject==NULL) return;

  if(PSS)
  {
    string str;
    vecBrush::prepExport(PSS);
    vecPen::prepExport(PSS);
    PS_Attr(str,PSS);
    PSS->inPath = 1;
    if(!str.isEmpty())
        fwrite(str(),str.length(),1,F);
  }
  while(IterObject!=NULL)
  {
    IterObject->Export2EPS(F,PSS);
    IterObject = IterObject->NextObject;
    if(PSS) PSS->inPath = 2;
  }
  if(PSS)
  {
    if(Close) fputs("\nclosepath",F);
    if(PSS->FillPattern==FILL_NONE)
      fputs("\nstroke",F);
    else
    {
      string str;
      FloatBBox bbx;
      CalcBoundingBox(bbx);
      InitBBox(bbx);
      FillObjectPS(bbx.MinX,bbx.MaxX,bbx.MinY,bbx.MaxY,str,PSS);
      if(!str.isEmpty())
        fwrite(str(),str.length(),1,F);
    }
    PSS->inPath = 0;
  }
}


/////////////////////////////////////////////////////////////


VectorImage::VectorImage(VectorList &VecList, const PS_State & NewPSS): PSS(NewPSS)
{
  //PSS = NewPSS;
  UsageCount = 0;
  FirstObject = VecList.FirstObject;	VecList.FirstObject = NULL;
  LastObject = VecList.LastObject;	VecList.LastObject = NULL;
  VectorObjects = VecList.VectorObjects; VecList.VectorObjects = 0;

  VectorObject *IterObject = FirstObject;
  while(IterObject!=NULL)
  {
    IterObject->UpperCont = this;
    IterObject = IterObject->NextObject;
  }
}


VectorImage::VectorImage()
{
  // PSS member object has its own ctor.
  UsageCount = 0;
}


/////////////////////////////////////////////////////////////


TextContainer::TextContainer()
{
  Text = NULL;
  TextObjects = 0;
  FontOrientation = 0;
  RotCenterX = RotCenterY = 0;
  TextAlign = 0;
  MixMode = 0;
}


TextContainer::~TextContainer()
{
  if(Text!=NULL)
  {
    for(int i=0; i<TextObjects; i++)
    {
      if(Text[i] != NULL)
      {
        delete(Text[i]);
        Text[i] = NULL;
      }
    }
    free(Text);
    Text = NULL;
  }  
  TextObjects = 0;
}


VectorObject *TextContainer::Duplicate(void) const
{
  TextContainer *pTC = new TextContainer();
  pTC->PosX = PosX;
  pTC->PosY = PosY;
  pTC->FontOrientation = FontOrientation;	///< Font rotation [deg]
  pTC->RotCenterX = RotCenterX;
  pTC->RotCenterY = RotCenterY;
  pTC->TextAlign = TextAlign;
  pTC->MixMode = MixMode;
  if(Text!=NULL)
  {
    pTC->Text = (TextObject**)malloc(sizeof(TextObject*)*TextObjects);
    for(int i=0; i<TextObjects; i++)
    {
      pTC->Text[i] = (Text[i]==NULL) ? NULL : new TextObject(*Text[i]);
    }
  }
return pTC;
}


/// This function discards all accents from a postscript text line.
/// Such line could be used for metrics calculation.
temp_string ExtractAccents(const string &LineStr)
{
  temp_string LineNoAccent = replacesubstring(LineStr,") show (","");
  LineNoAccent = replacesubstring(LineNoAccent,") accentshow (","");
  LineNoAccent = replacesubstring(LineNoAccent,") ACCENTSHOW (","");
  LineNoAccent = replacesubstring(LineNoAccent,"\\302)(","");		// acute
  LineNoAccent = replacesubstring(LineNoAccent,"\\317)(","");		// charon
  LineNoAccent = replacesubstring(LineNoAccent,"\\312)(","");		// ring
  LineNoAccent = replacesubstring(LineNoAccent,"\\301)(","");		// grave
  LineNoAccent = replacesubstring(LineNoAccent,"\\310)(","");		// umlaut
  LineNoAccent = replacesubstring(LineNoAccent,"\\47)(","");		 //insert space before apostrophe \\47
return LineNoAccent;
}


/// Append show only when needed. The intent is not to emit "() show". Too much empty
/// strings only bloat image. Of course that "(\()" is a string containing one character.
/// @param[in,out]	str	String that needs either cleanup or adding \show.
/// @param[in,out]	strLine Newly added chunk of text. Will be erased on return.
/// @param[in]		HorizontalPos	Horizontal position placement.
void AppendShow(string &str, string &strLine, const unsigned char HorizontalPos, const string &Override)
{
  int len = length(strLine);
  if(len==0) return;
  if(len>=2)
    {
    if(strLine[len-2]=='(' && strLine[len-1]==')')
      {
      if(len>=3 && strLine[len-3]!='\\')		// Is the curly brace preffixed?
        {
        if(len>=3 && strLine[len-3]==' ') len--;	//Remove orphaned space.
        strLine = copy(strLine,0,len-2);
        str += strLine;
	erase(strLine);
        return;
        }
      }
    }

  switch((HorizontalPos & 6))
    {
    case 2: if(strstr(strLine,") show (")!=NULL)		// TA_RIGHT
	    {
	      str += ExtractAccents(strLine);
	      str += "\nstringwidth pop -1 mul 0 rmoveto";
	      str += strLine;
            }
	    else
            {
	      str += strLine;
	      str += "\ndup stringwidth pop -1 mul 0 rmoveto";
	    }
	    break;
    case 6: if(strstr(strLine,") show (")!=NULL)		// TA_CENTER
	    {
	      str += ExtractAccents(strLine);
	      str += "\nstringwidth pop -2 div 0 rmoveto";
	      str += strLine;
	    }
	    else
            {
	      str += strLine;
	      str += "\ndup stringwidth pop -2 div 0 rmoveto";
            }
	    break;
    default: str += strLine;
    }

  str += Override;

  str += "\nshow";
  erase(strLine);
}


temp_string TextContainer::Export2EPS(PS_State *PSS) const
{
string str, strLine;
RGB_Record BkTextColor;
float CurPosX = PosX;
float CurPosY = PosY;
const bool TextRotated = fabs(NormAngleDEG(FontOrientation)) > 0.01;
string Override;

  if(Text!=NULL && TextObjects>0)
  {    
    if(PSS!=NULL)
    {    
      PS_Attr(str,PSS);
      memcpy(&BkTextColor,&PSS->LineColor,sizeof(RGB_Record));
      if(MixMode==2)
          Override.printf("\ngsave dup"
			"\ntrue charpath pathbbox"	//llx lly urx ury
			"\n3 index"			//llx lly urx ury llx
			" 3 index"			//llx lly urx ury llx lly
			"\nmoveto"			//llx lly urx ury (llx lly)
			"\n1 index"			//llx lly urx ury urx
			" 3 index"			//llx lly urx ury urx lly
			"\nlineto"			//llx lly urx ury (urx lly)
			"\n1 index"
			" 1 index"
			"\nlineto"			//llx lly urx ury (urx ury)
			"\n3 index"
			" 1 index"
			"\nlineto"
			"\npop pop pop pop"
			"\nclosepath"
			"\n%0.2g %0.2g %0.2g setrgbcolor"
			"\nfill grestore",
			  (float)PSS->FillBackground.Red/256.0,
			  (float)PSS->FillBackground.Green/256.0,
			  (float)PSS->FillBackground.Blue/256.0);
    }
    if(TextRotated)
    {
      str += "\ngsave";
      str.cat_printf("\n%2.2f %2.2f translate", CurPosX+RotCenterX, CurPosY+RotCenterY);
      str.cat_printf("\n%g rotate", FontOrientation);
      str.cat_printf("\n%2.2f %2.2f translate", -CurPosX-RotCenterX, -CurPosY-RotCenterY);
    }

    for(int i=0; i<TextObjects; i++)
    {
      if(Text[i]!=NULL)
      {
        FixPsAccent(Text[i]->contents,"\\'{","(\\302)");	// acute
        FixPsAccent(Text[i]->contents,"\\v{","(\\317)");	// charon
        FixPsAccent(Text[i]->contents,"\\r{","(\\312)");	// ring
        FixPsAccent(Text[i]->contents,"\\`{","(\\301)");	// grave
        FixPsAccent(Text[i]->contents,"\\\"{","(\\310)");	// umlaut
        FixPsAccent2(Text[i]->contents,"\\accent39","( \\47)"); //insert space before apostrophe \\47
        
        if(i>0) AppendShow(str,strLine,TextAlign,Override);
	
        if(PSS!=NULL)
        {
          if(memcmp(&Text[i]->TextColor,&PSS->LineColor,sizeof(RGB_Record)) != 0) 
          {			// EPS does not handle separatelly text color and line color.
            PSS->dirty |= PSS_LineColor;
            memcpy(&PSS->LineColor,&Text[i]->TextColor,sizeof(RGB_Record));
            PS_Attr(str,PSS);
          }
        }

        if(i==0 || fabs(Text[i]->size-Text[i-1]->size)>1e-3 || Text[i]->TargetFont!=Text[i-1]->TargetFont)
        {          
	  if(i>0 || PSS==NULL || PSS->FontSize!=Text[i]->size || PSS->FontName!=Text[i]->TargetFont)
          {          
	    str += "\n";
	    Font2PS(str, Text[i]->size*2.66f, Text[i]->TargetFont);
            if(!TextRotated && PSS!=NULL)
	    {
              PSS->FontSize = Text[i]->size;
	      PSS->FontName = Text[i]->TargetFont;
	    }
          }
        }

        if(i==0)
        {
          str.cat_printf("\nnewpath %2.2f %2.2f moveto", CurPosX, CurPosY);
        }

        char *ScanS = Text[i]->contents();
	char *ScanS2;
	while((ScanS2=StrChr(ScanS,'\n')) !=  NULL)
	{
	  *ScanS2 = 0;
          //if(ScanS!=NULL && *ScanS!=0)
	  {
            strLine += "\n(";	
            strLine += ScanS;
	    strLine += ')';
          }
	  *ScanS2 = '\n';
	  ScanS = ScanS2 + 1;

	  CurPosY -= 1.01f * Text[i]->size*2.66f;
	  AppendShow(str,strLine,TextAlign,Override);
	  str.cat_printf(" newpath %2.2f %2.2f moveto", CurPosX, CurPosY);
	}

	//if(ScanS!=NULL && *ScanS!=0)
	{
          strLine += "\n(";	
          strLine += ScanS;
	  strLine += ')';
        }
      }      
    }
    AppendShow(str,strLine,TextAlign,Override);
    if(TextRotated)
    {    
      str += "\ngrestore";
      if(PSS)   
        memcpy(&PSS->LineColor,&BkTextColor,sizeof(RGB_Record));
    }
  }

return temp_string(str);
}


void TextContainer::CalculateExtent(float &TextWidth, float &TextHeight) const
{
float CurPosX = 0;
float LastLineSz = 0;

  TextWidth = TextHeight = 0;
  if(Text!=NULL && TextObjects>0)
  {
    for(int i=0; i<TextObjects; i++)
    {
      if(Text[i]!=NULL)
      {
	LastLineSz = Text[i]->size;
	unsigned j = 0;

	string str;
	while(j < Text[i]->contents.length())
        {
	  char ch = Text[i]->contents[j];
	  if(ch=='\\')
	  {
	    j++;
            while(j < Text[i]->contents.length())
	    {
	      ch = Text[i]->contents[j];
	      j++;
	      if(ch==' ') break;
	      if(ch=='{')
	      {
	        while(j < Text[i]->contents.length())
		{
		  ch = Text[i]->contents[j];
	          j++;
		  if(ch=='}') break;
		  str += ch;
		}
	        break;
	      }
	    }
	    continue;
	  }
	  str += ch;
	  j++;
        }

	j = 0;
	while(j < str.length())
	{
          char ch = str[j];
          switch(ch)
	  {
	    case '\n': if(CurPosX>TextWidth) TextWidth=CurPosX;
	               CurPosX = 0;
	               TextHeight += LastLineSz;
		       break;
	    case 'l':
	    case 'i':  CurPosX += 0.26f * LastLineSz;
		       break;
	    case 'm':  CurPosX += 0.71f * LastLineSz;
		       break;
            default:   if(isupper(ch))
		         CurPosX += 0.5f * LastLineSz;
		       else
                         CurPosX += 0.44f * LastLineSz;
		       break;
	  }
	  j++;
	}
      }
    }
    if(CurPosX>TextWidth) TextWidth=CurPosX;
    TextHeight += LastLineSz;
  }
}


#ifdef _DEBUG
void TextContainer::Dump2File(FILE *F) const
{
  fprintf(F,"TextContainer{[%.2f;%.2f]", PosX, PosY);
  for(int i=0; i<TextObjects; i++)
    {
      if(Text[i]!=NULL)
      {
      fprintf(F,"FontSz=%.3f;", Text[i]->size);
      fprintf(F,"%s\"%s\"", (i==0)?"":",", Text[i]->contents());
      }
    }
  fputs("}\n",F);
}
#endif


void TextContainer::Transform(const AbstractTransformXY &Tx)
{
  RotCenterX += PosX;
  RotCenterY += PosY;

  float ScaleX=RotCenterX+0.1, ScaleY=RotCenterY+0.1;
  Tx.ApplyTransform(ScaleX, ScaleY);

  Tx.ApplyTransform(RotCenterX, RotCenterY);
  ScaleX = fabs(RotCenterX-ScaleX) / 0.1;	// This is translation invariant.

  Tx.ApplyTransform(PosX, PosY);
  RotCenterX -= PosX;
  RotCenterY -= PosY;

	// Scale also font size.
  for(int i=0; i<TextObjects; i++)
  {
    if(Text[i]==NULL) continue;
    Text[i]->size *= ScaleX;
  }
}


void TextContainer::AddText(temp_string contents, const PS_State &PSS)
{
  const char *PsFontName;  

  //printf("|%s|",contents());
  if(PSS.FontItallic)
    PsFontName = (PSS.FontWeight>=500) ? "Times-BoldItalic" : "Times-Italic";
  else
    PsFontName = (PSS.FontWeight>=500) ? "Times-Bold" : "Times-Roman";
  
  AddText(contents, PsFontName, PSS);
}


void TextContainer::AddText(temp_string contents, const char *font, const PS_State &PSS)
{
TextObject *pTobj;
  if(contents.length()<=0) return;

	// The most signifficant character replacements, ), ( and \ are special characters.
  contents = replacesubstring(contents,"(","\\(");
  contents = replacesubstring(contents,")","\\)");

  if(Text==NULL || TextObjects<=0)
  {
    Text = (TextObject**)malloc(sizeof(TextObject*));
    if(Text == NULL)
    {	/// @TODO - report a memory problem.
      return;
    }
    pTobj = Text[0] = new TextObject;
    pTobj->TargetFont = font;
    pTobj->contents = contents;
    pTobj->size = PSS.FontSize;
    //pTobj->Weight = Weight;
    memcpy(&pTobj->TextColor,&PSS.TextColor,sizeof(RGB_Record));
    TextObjects = 1;
    return;
  }

  pTobj = Text[TextObjects-1];
  if(pTobj->TargetFont==font && fabs(pTobj->size-PSS.FontSize)<1e-4 && 
     memcmp(&PSS.TextColor,&pTobj->TextColor,sizeof(RGB_Record))==0)	//&& pTobj->Weight==Weight
  {
    pTobj->contents += contents;
    return;
  }

  TextObjects++;
  TextObject **tmp = (TextObject**)realloc(Text, (TextObjects)*sizeof(TextObject*));
  if(tmp==NULL) 
    {	/// @TODO - report a memory problem.
    TextObjects--;
    return;
    }
  Text = tmp;
  pTobj = Text[TextObjects-1] = new TextObject;
  pTobj->TargetFont = font;
  pTobj->contents = contents;
  
  pTobj->size = PSS.FontSize;
  //pTobj->Weight = Weight;  
  memcpy(&pTobj->TextColor,&PSS.TextColor,sizeof(RGB_Record));
}


bool TextContainer::isEmpty(void) const
{
  if(Text==NULL || TextObjects<=0) return true;
  for(int i=0; i<TextObjects; i++)
    {
    if(Text[i]!=NULL && Text[i]->contents.length()>0) 
      return false;
    }
  return true;
}


static void CheckAccent(uint32_t & Feature, const char *PS_text)
{
  if(PS_text==NULL) return;
  if(StrStr(PS_text,"accentshow")!=NULL)  Feature |= EPS_accentshow;
  if(StrStr(PS_text,"ACCENTSHOW")!=NULL)  Feature |= EPS_ACCENTSHOW;
}


void TextContainer::FeaturesEPS(uint32_t & Feature) const
{
  CheckAccent(Feature, Export2EPS()());
}


/////////////////////////////////////////////////////////////

#include "typedfs.h"
#include "raster.h"


void Color2PS(string & Text, const RGB_Record & TextColor);


static void FixPsAccent2(string & s, const char *TexAccent, const char *PsAccent)
{
string Text2;
const char *pos;

 while((pos=strstr(s(),TexAccent)) != NULL)
   {
   if(pos>s())
     {
     if(pos[-1]=='(')
       Text2 = copy(s,0,pos-s()-1);  // Discard empty text before
     else
       {
       Text2 = copy(s,0,pos-s());
       Text2 += ") show ";	// Close previous text path
       }
     }

   pos += strlen(TexAccent);
   Text2 += PsAccent;
   Text2 += '(';   
   Text2 += *pos;	pos++;
   Text2 += ") accentshow (";     
   if(*pos!=0)
       Text2 += pos;		// Append a rest of the string.

   s = Text2;   
   }
}


/** Transfer accent to PostSctipt text.
 * @param[in,out] s		String with text to be fixed.
 * @param[in]	TexAccent	Accent prefix in LaTeX.
 * @param[in]   PsAccent	Accent equivalent in PS.*/
static void FixPsAccent(string & s, const char *TexAccent, const char *PsAccent)
{
string Text2;
const char *pos;

 while((pos=strstr(s(),TexAccent)) != NULL)
   {
   bool CharIsUpper = false;
   if(pos>s())
     {
     if(pos[-1]=='(')
       Text2 = copy(s,0,pos-s()-1);  // Discard empty text before
     else
       {
       Text2 = copy(s,0,pos-s());
       Text2 += ") show ";	// Close previous text path
       }
     }

   if(Text2.length()<=0 && PsAccent!=NULL && PsAccent[0]=='(')
     Text2 += PsAccent + 1;
   else
     Text2 += PsAccent;
   Text2 += "(";
   pos += 3;
   while(*pos!='}')
     {
     if(*pos==0) break;
     if(isupper(*pos)) CharIsUpper = true;
     Text2 += *pos++;
   }
   if(CharIsUpper) Text2 += ") ACCENTSHOW";
              else Text2 += ") accentshow";
   Text2 += " (";
   if(*pos!=0)
     Text2 += pos+1;		// Append a rest of the string.

   s = Text2;
   }
}


/** Calculate amount of lines in a given text blob.
 * @return	Amount oflines. */
int LineCountTxt(const char *Txt)
{
int LineCount = 1;

  while((Txt=strchr(Txt,'\n')) != NULL)
  {
    Txt++;
    LineCount++;
  }
  return LineCount;
}


/** Convert accents to PS equivalents.
 * @param[in,out]	s	EPS string to be fixed. */
/*
static void FixPsSymbol(string & s, const char *TeXSymb, const char PSsymb,
                        const string &CurrentFont, const string &SymbolFont)
{
string Text2;
const char *pos;

 while((pos=strstr(s(),TeXSymb)) != NULL)
   {
   if(pos>s())
     {
     if(pos[-1]=='(')
       Text2 = copy(s,0,pos-s()-1);  // Discard empty text before
     else
       {
       Text2 = copy(s,0,pos-s());
       Text2 += ") show ";	// Close previous text path
       }
     }
   else
     Text2 = ") show ";

   Text2 += SymbolFont;
   Text2 += "(";
   Text2 += PSsymb;
   Text2 += ") show\n";

   Text2 += CurrentFont;
   pos += StrLen(TeXSymb);

   Text2 += " (";
   if(*pos!=0)
     Text2 += pos;		// Append a rest of the string.

   s = Text2;
   }
}
*/


/// Emit Postscript command for line type change.
void PS_Attr(string & PSData, PS_State *PSS)
{
 if(PSS->dirty & PSS_LineColor)
   {
   PSData.cat_printf("\n%0.2g %0.2g %0.2g setrgbcolor",
	(float)PSS->LineColor.Red/256.0,
	(float)PSS->LineColor.Green/256.0,
	(float)PSS->LineColor.Blue/256.0);
   PSS->dirty &= ~PSS_LineColor;
   }
 if(PSS->dirty & PSS_LineStyle)
   {
   switch(PSS->LineStyle)
	{
	case 2:PSData+="\n[12 4]0"; break;
	case 3:PSData+="\n[2 2]0"; break;
	case 4:PSData+="\n[8 3 2 3]0"; break;
	case 5:PSData+="\n[8 8]0"; break;
	case 6:PSData+="\n[6 2 2 2 2 2]0"; break;
	case 7:PSData+="\n[4 4]0"; break;
	case 8:PSData+="\n[8 2 3 2]0"; break;
	case 9:PSData+="\n[14 2]0"; break;
	case 10:PSData+="\n[1 1]0"; break;
	case 11:PSData+="\n[1 3]0"; break;
	case 12:PSData+="\n[3 5]0"; break;
	case 13:PSData+="\n[8 2 4 2]0"; break;
	case 14:PSData+="\n[2 2 10 2]0"; break;
	case 15:PSData+="\n[10 2 2 2]0"; break;
	case 16:PSData+="\n[10 2 2 2 2 2]0"; break;	//type No13 in wpg2
	case 17:PSData+="\n[12 4 12 4 4 4]0"; break;	//type No14 in wpg2
	case 18:PSData+="\n[12 2 12 2 3 2 3 2]0"; break;//type No15 in wpg2
	case 19:PSData+="\n[1 2 1 2 1 5]0"; break;	//type No7 in wpg2
	case 1:
	//case 0: //no line
	default:PSData+="\n[]0";
	}
   PSData+=" setdash";
   PSS->dirty &= ~PSS_LineStyle;
   }
 if(PSS->dirty & PSS_LineCap)
  {
  PSData.cat_printf("\n%u setlinecap",PSS->LineCap);
  PSS->dirty &= ~PSS_LineCap;
  }
 if(PSS->dirty & PSS_LineJoin)
  {
  PSData.cat_printf("\n%u setlinejoin",PSS->LineJoin);
  PSS->dirty &= ~PSS_LineJoin;
  }
 if(PSS->dirty & PSS_LineWidth)
  {
  PSData.cat_printf("\n%2.2f setlinewidth",PSS->LineWidth);
  PSS->dirty &= ~PSS_LineWidth;
  }
}


void FillObjectPS(float MinPolyX, float MaxPolyX, float MinPolyY, float MaxPolyY, string & PSData, PS_State *PSS)
{
string tmp, color;
float Distance;

  if(PSS==NULL || PSS->FillPattern==FILL_NONE)
  {
    PSData += "\nstroke";		//draw frame line
    return;
  }

  if(PSS->FirstTimeFix &&
     PSS->FillColor.Red == PSS->FillBackground.Red &&
     PSS->FillColor.Green == PSS->FillBackground.Green &&
     PSS->FillColor.Blue == PSS->FillBackground.Blue)
    {
    PSS->FillColor.Red = ~PSS->FillColor.Red;
    PSS->FillColor.Green = ~PSS->FillColor.Green;
    PSS->FillColor.Blue = ~PSS->FillColor.Blue;
    PSS->FirstTimeFix = false;
    }

  if(PSS->FillPattern >= (0x80|FILL_SOLID))
  {
     color.printf("\n%0.2g %0.2g %0.2g setrgbcolor",
		(float)PSS->FillBackground.Red/256.0,
	 	(float)PSS->FillBackground.Green/256.0,
		(float)PSS->FillBackground.Blue/256.0);
    PSData += "\ngsave";
    PSData += color;
    PSData += "\nfill";
    PSData+=" grestore";
    erase(color);
  }

  color.printf("\n%0.2g %0.2g %0.2g setrgbcolor",
		(float)PSS->FillColor.Red/256.0,
	 	(float)PSS->FillColor.Green/256.0,
		(float)PSS->FillColor.Blue/256.0);

  switch(PSS->FillPattern & 0x7F)
  {
    case FILL_SOLID:if(PSS->LineStyle!=0)
			 PSData+="\ngsave";
		       PSData+=color;
		       PSData+="\nfill";
		       if(PSS->LineStyle!=0)
			 PSData+=" grestore";
		       else
			 PSS->dirty |= PSS_LineColor;
		       break;

    case FILL_DIAG_UPx:Distance = PSS->HatchDistance;
		       goto FINALDiagonal;
    case FILL_DIAG_UP: Distance = 2.8f;
		       goto FINALDiagonal;
    case FILL_DIAG_UP2:Distance = 5.7f;
		       goto FINALDiagonal;
    case FILL_DIAG_UP4:Distance = 11.3f;
FINALDiagonal:	       tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto %2.2f %2.2f rlineto}",  // Diagonal up
			       MinPolyX-(MaxPolyY-MinPolyY),Distance,MaxPolyX,MinPolyY,
			       MaxPolyY-MinPolyY, MaxPolyY-MinPolyY  );
		       goto FINALLY;

    case FILL_DIAG_CROSSHATCHx:Distance = PSS->HatchDistance;
		       goto FINALCross;
    case FILL_DIAG_CROSSHATCH:Distance=2.8f;
		       goto FINALCross;
    case FILL_DIAG_CROSSHATCH2:Distance=5.7f;
		       goto FINALCross;
    case FILL_DIAG_CROSSHATCH4:Distance=11.3f;
FINALCross:	       tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto %2.2f %2.2f rlineto}for",
			       MinPolyX-(MaxPolyY-MinPolyY),Distance,MaxPolyX,MinPolyY,
			       MaxPolyY-MinPolyY, MaxPolyY-MinPolyY );
			PSData+='\n';
			if(PSS->LineStyle!=0) PSData+="gsave";
			PSData+=" clip newpath 0 setlinewidth\n";
			PSData+=color;
			PSData+=tmp;
			tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto %2.2f %2.2f rlineto}",
			       MinPolyX,Distance,(MaxPolyX+(MaxPolyY-MinPolyY)),MinPolyY,
			       -(MaxPolyY-MinPolyY), MaxPolyY-MinPolyY  );
			goto FINALLY2;

    case FILL_VERTICALx:Distance = PSS->HatchDistance;
			goto FINALVertical;
    case FILL_VERTICAL: Distance=2;
			goto FINALVertical;
    case FILL_VERTICAL2: Distance=4;
			goto FINALVertical;
    case FILL_VERTICAL4:Distance=8;
FINALVertical:		tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto 0 %2.2f rlineto}",
			       MinPolyX,Distance,MaxPolyX,MinPolyY,
			       (MaxPolyY-MinPolyY));
FINALLY:		PSData+='\n';
			if(PSS->LineStyle!=0) PSData+="gsave";
			PSData += " clip newpath 0 setlinewidth\n";
			PSData += color;
FINALLY2:		PSData += tmp;
			PSData += "for\nstroke";
			if(PSS->LineStyle!=0) PSData+=" grestore";
			break;

    case FILL_GRAY_3:	Distance=16;
FINALDot:		tmp.printf("\n[0.5 %2.1f] 0 setdash\n %2.2f %2.1f %2.2f {%2.2f moveto 0 %2.2f rlineto %2.2f %2.2f rmoveto 0 %2.2f rlineto}",
			       Distance-0.5,
			       MinPolyX,Distance,MaxPolyX,
			       MinPolyY,
			       MaxPolyY-MinPolyY,
			       Distance/2.0,-Distance/2.0 - (MaxPolyY-MinPolyY),
			       (MaxPolyY-MinPolyY)+Distance/2.0  );
			goto FINALLY;

    case FILL_GRAY_6: Distance=8;
			goto FINALDot;
    case FILL_GRAY_12:Distance=4;
			goto FINALDot;
    case FILL_GRAY_25:Distance=2;
			goto FINALDot;
    case FILL_GRAY_50:Distance=1;
			goto FINALDot;
    case FILL_GRAY_75:Distance=0.75;
			goto FINALDot;

    case FILL_DIAG_DOWN:Distance=2.8f;
FinalDiagDown:		tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto %2.2f %2.2f rlineto}",
			       MinPolyX,Distance,(MaxPolyX+(MaxPolyY-MinPolyY)), MinPolyY,
			       -(MaxPolyY-MinPolyY), MaxPolyY-MinPolyY);
			goto FINALLY;
    case FILL_DIAG_DOWN2:Distance = 5.7f;
			goto FinalDiagDown;
    case FILL_DIAG_DOWN4:Distance = 11.4f;
			goto FinalDiagDown;
    case FILL_DIAG_DOWNx:Distance=PSS->HatchDistance;
			goto FinalDiagDown;

    case FILL_HORIZONTAL:Distance=2;
FINALHorizontal:	 tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f exch moveto %2.2f 0 rlineto}",
			       MinPolyY,Distance,MaxPolyY, MinPolyX,
			       (MaxPolyX-MinPolyX));
			goto FINALLY;
    case FILL_HORIZONTAL2:Distance=4;
			goto FINALHorizontal;
    case FILL_HORIZONTAL4:Distance=8;
			goto FINALHorizontal;
    case FILL_HORIZONTALx:Distance=PSS->HatchDistance;
			goto FINALHorizontal;
			

    case FILL_CROSS_HATCH:Distance=2;
FINALHrzCross:		 tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f exch moveto %2.2f 0 rlineto}for",
			       MinPolyY,Distance,MaxPolyY, MinPolyX,
			       (MaxPolyX-MinPolyX));
			PSData+='\n';
			if(PSS->LineStyle!=0) PSData+="gsave";
			PSData+=" clip newpath 0 setlinewidth\n";
			PSData+=color;
			PSData+=tmp;
			tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto 0 %2.2f rlineto}",
			       MinPolyX,Distance,MaxPolyX, MinPolyY,
			       (MaxPolyY-MinPolyY));
			goto FINALLY2;
    case FILL_CROSS_HATCH2:Distance=4;
			goto FINALHrzCross;
    case FILL_CROSS_HATCH4:Distance=8;
			goto FINALHrzCross;
    case FILL_CROSS_HATCHx:Distance=PSS->HatchDistance;
			goto FINALHrzCross;

    case FILL_SQUARES:  tmp = "BoxFill1";
FillPattern:		PSData+="\ngsave clip\n"
				 "[/Pattern /DeviceRGB] setcolorspace";
			PSData += copy(color,0,color.length()-11);
			PSData+=tmp + " setpattern\n"
				"fill\n"
				"grestore";
			if(PSS->LineStyle==0) PSData+="\nstroke";
			break;
    case FILL_SQUARES2: tmp = "BoxFill2";
			goto FillPattern;
    case FILL_PLUS:	tmp = "PlusFill";
			goto FillPattern;
    case FILL_BALLS:	tmp = "BallsFill";
			goto FillPattern;
    case FILL_BRICKS:	tmp = "BrickFill";
			goto FillPattern;
    case FILL_DIAG_BRICKS:tmp = "DiagBrickFill";
			goto FillPattern;
    case FILL_HONEYCOMB:tmp = "HoneycombFill";
			goto FillPattern;
    case FILL_FISHSCALE:tmp = "FishscaleFill";
			goto FillPattern;
    case FILL_ARCH:	tmp = "ArchFill";
			goto FillPattern;
    case FILL_CHAINLINK:tmp = "ChainlinkFill";
			goto FillPattern;
    case FILL_PATIO:	tmp = "PatioFill";
			goto FillPattern;
    case FILL_WEAVE:	tmp = "WeaveFill";
			goto FillPattern;
    case FILL_WAVES:	tmp = "WavesFill";
			goto FillPattern;
    case FILL_TARTAN:	Distance=10;
			PSData += '\n';
			if(PSS->LineStyle!=0) PSData+="gsave";
			PSData+=" clip newpath 0 setlinewidth\n";
			PSData+=color;
			tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f exch moveto %2.2f 0 rlineto",
			       MinPolyY,Distance,MaxPolyY, MinPolyX,
			       (MaxPolyX-MinPolyX));
			PSData += tmp;
			tmp.printf(" 0 %2.2f rmoveto %2.2f 0 rlineto",
			       Distance/6, -(MaxPolyX-MinPolyX));
			PSData += tmp;
			tmp.printf(" 0 %2.2f rmoveto %2.2f 0 rlineto}for",
			       Distance/6, (MaxPolyX-MinPolyX));
			PSData += tmp;

			tmp.printf("\n%2.2f %2.1f %2.2f {%2.2f moveto 0 %2.2f rlineto",
			       MinPolyX,Distance,MaxPolyX, MinPolyY,
			       (MaxPolyY-MinPolyY));
			PSData += tmp;
			tmp.printf(" %2.2f 0 rmoveto 0 %2.2f rlineto",
			       Distance/6, -(MaxPolyY-MinPolyY));
		        PSData += tmp;
			tmp.printf(" %2.2f 0 rmoveto 0 %2.2f rlineto}",
			       Distance/6, (MaxPolyY-MinPolyY));
			goto FINALLY2;

    case FILL_TRIANGLES:tmp = "TriangleFill";
			 goto FillPattern;
    case FILL_SM_SQUARES:tmp = "SmSquareFill";
			 goto FillPattern;
  }

  if(PSS->LineStyle!=0)
    PSData+="\nstroke";		//draw frame line
}
